﻿using System.Collections.Generic;
using System.Linq;
using Viyi.Util.Linq;
using Xunit;

namespace Viyi.Util.Test.Linq
{
    public partial class TestMaxMin
    {
        private readonly Model<int>[] intModels = Model.From(new int[]
        {
            1, 2, 3, 4, 5,
            int.MinValue,
            6, 7, 8, 9, 10,
            int.MaxValue,
            int.MinValue,
            11, 12, 13, 14, 15
        });

        [Fact]
        public void TestMaxMinUInt64()
        {
            var data = Model.From(new ulong[]
            {
                1, 2, 3, 4, 5,
                ulong.MinValue,
                ulong.MaxValue,
                6, 7, 8, 9, 10
            });

            Assert.Equal(ulong.MaxValue, data.ByMax(m => m.Value).Value);
            Assert.Equal(ulong.MinValue, data.ByMin(m => m.Value).Value);

            Assert.Equal(ulong.MaxValue,
                Model.From(new[] {
                    ulong.MaxValue
                }).ByMax(m => m.Value).Value);

            Assert.Equal(ulong.MaxValue,
                Model.From(new[] {
                    ulong.MaxValue
                }).ByMin(m => m.Value).Value);

            Assert.Equal(ulong.MaxValue,
                Model.From(new[] {
                    ulong.MaxValue, ulong.MinValue
                }).ByMax(m => m.Value).Value);

            Assert.Equal(ulong.MinValue,
                Model.From(new[] {
                    ulong.MaxValue, ulong.MinValue
                }).ByMin(m => m.Value).Value);
        }

        [Fact]
        public void TestMaxMinUInt32()
        {
            var data = Model.From(new int[]
            {
                1, 2, 3, 4, 5,
                int.MinValue,
                int.MaxValue,
                6, 7, 8, 9, 10
            });

            Assert.Equal(int.MaxValue, data.ByMax(m => m.Value).Value);
            Assert.Equal(int.MinValue, data.ByMin(m => m.Value).Value);

            Assert.Equal(int.MaxValue,
                Model.From(new[] {
                    int.MaxValue
                }).ByMax(m => m.Value).Value);

            Assert.Equal(int.MaxValue,
                Model.From(new[] {
                    int.MaxValue
                }).ByMin(m => m.Value).Value);

            Assert.Equal(int.MaxValue,
                Model.From(new[] {
                    int.MaxValue, int.MinValue
                }).ByMax(m => m.Value).Value);

            Assert.Equal(int.MinValue,
                Model.From(new[] {
                    int.MaxValue, int.MinValue
                }).ByMin(m => m.Value).Value);
        }

        [Fact]
        public void TestCompareFunc()
        {
            var max = intModels.CompareToChoose((m1, m2) => m1.Value > m2.Value);
            Assert.Equal(int.MaxValue, max.Value);

            var min = intModels.CompareToChoose((m1, m2) => m1.Value < m2.Value);
            Assert.Equal(int.MinValue, min.Value);
        }

        [Fact]
        public void TestComparer()
        {
            // 直接用 m1 - m2 可能造成溢出，使结果不准确
            var comparer = new LabmdaComparer<Model<int>>((m1, m2) =>
            {
                return m1.Value > m2.Value
                    ? 1
                    : (m1.Value < m2.Value ? -1 : 0);
            });

            var max = intModels.Max(comparer);
            Assert.Equal(int.MaxValue, max.Value);

            var min = intModels.Min(comparer);
            Assert.Equal(int.MinValue, min.Value);
        }
    }
}

namespace Viyi.Util.Test.Linq
{
    // Nested classes

    public partial class TestMaxMin
    {
        private static class Model
        {
            public static Model<T>[] From<T>(IEnumerable<T> values)
            {
                return values
                    .Select((v, i) => new Model<T>(i, v))
                    .ToArray();
            }
        }

        private class Model<T>
        {
            public int Index { get; }
            public T Value { get; }

            public Model(int index, T value)
            {
                Index = index;
                Value = value;
            }
        }
    }
}
